package web

import (
	"errors"
	"gitee.com/DonHz/basic-go/webook/internal/domain"
	"gitee.com/DonHz/basic-go/webook/internal/service"
	ijwt "gitee.com/DonHz/basic-go/webook/internal/web/jwt"
	"gitee.com/DonHz/basic-go/webook/pkg/logger"
	"github.com/ecodeclub/ekit/slice"
	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
	"net/http"
	"strconv"
	"time"
)

var _ handler = (*ArticleHandler)(nil)

type ArticleHandler struct{
	svc service.ArticleService
	l logger.Logger
	interSvc service.InteractiveService
	biz string
}

func NewArticleHandler(svc service.ArticleService, l logger.Logger) *ArticleHandler{
	return &ArticleHandler{
		svc: svc,
		l:l,
		biz: "articles",
	}
}

func (h *ArticleHandler) RegisterRoutes(r *gin.Engine) {
	g:=r.Group("/articles")
	g.POST("/edit", h.Edit)
	g.POST("/publish", h.Publish)
	g.POST("/withdraw", h.Withdraw)
	g.POST("/list", h.List)
	g.GET("/detail/:id", h.Detail)

	pub:=g.Group("/pub")
	pub.GET("/:id", h.PubDetail)
	pub.POST("/like", h.Like)
}

func (h *ArticleHandler) Edit(ctx *gin.Context){
	var req ArticleReq
	if err:=ctx.Bind(&req); err!=nil {
		h.l.Error("Edit 请求获取异常", logger.Error(err))
		return
	}
	// 优化：检测输入

	// 调用service
	c, ok := ctx.Get("claims")
	if !ok {
		ctx.String(http.StatusOK, "系统异常")
		h.l.Error("未获取到用户的session信息")
		return
	}
	claims, ok := c.(*ijwt.UserClaims)
	if !ok {
		ReturnError(ctx, 5, "系统错误")
		h.l.Error("未获取到用户的session信息")
		return
	}
	id, err:=h.svc.Save(ctx, req.toDomain(claims.Uid))
	if err!=nil {
		ReturnError(ctx, 5, "系统错误")
		h.l.Error("保存帖子失败", logger.Error(err))
		return
	}
	ReturnSuccess(ctx, 200, "保存成功", id, 1)
}

func (h *ArticleHandler) Publish(ctx *gin.Context){
	var req ArticleReq
	if err:=ctx.Bind(&req); err!=nil {
		h.l.Error("Publish 请求获取异常", logger.Error(err))
		return
	}
	c, ok := ctx.Get("claims")
	if !ok {
		ctx.String(http.StatusOK, "系统异常")
		h.l.Error("未获取到用户的session信息")
		return
	}
	claims, ok := c.(*ijwt.UserClaims)
	if !ok {
		ReturnError(ctx, 5, "系统错误")
		h.l.Error("未获取到用户的session信息")
		return
	}
	id, err:=h.svc.Publish(ctx, req.toDomain(claims.Uid))
	if err!=nil {
		ReturnError(ctx, 5, "系统错误")
		h.l.Error("发表帖子失败", logger.Error(err))
		return
	}
	ReturnSuccess(ctx, 200, "发表帖子成功", id, 1)
}

func (h *ArticleHandler) Withdraw(ctx *gin.Context){
	type Req struct {
		Id int64
	}
	var req Req
	if err:=ctx.Bind(&req); err!=nil{
		return
	}
	c:=ctx.MustGet("claims")
	claims, ok:=c.(*ijwt.UserClaims)
	if !ok {
		ReturnError(ctx, 5, "系统错误")
		h.l.Error("未获取到用户的session信息")
		return
	}

	err:=h.svc.Withdraw(ctx, domain.Article{
		Id: req.Id,
		Author: domain.Author{
			Id: claims.Uid,
		},
	})
	if err!=nil{
		ReturnError(ctx, 5, "系统错误")
	}
	ReturnSuccess(ctx, 200, "文章修改为仅个人可见成功", req.Id, 1)
}

func (h *ArticleHandler) List(ctx *gin.Context) {
	var req ListReq
	if err:=ctx.Bind(&req); err!=nil{
		return
	}
	c:=ctx.MustGet("claims")
	claims, ok:=c.(*ijwt.UserClaims)
	if !ok {
		ReturnError(ctx, 5, "系统错误")
		h.l.Error("未获取到用户的session信息")
		return
	}

	res, err:=h.svc.List(ctx, domain.Article{
		Author: domain.Author{
			Id: claims.Uid,
		},
	}, req.Offset, req.Limit)
	if err!=nil{
		ReturnError(ctx, 5, "系统错误")
		h.l.Error("获取文章列表异常 ", logger.Error(err))
		return
	}
	result:=Result{Data: slice.Map[domain.Article, ArticleVo](res,
		func(idx int, src domain.Article) ArticleVo {
			return ArticleVo{
				Id: src.Id,
				Title: src.Title,
				Abstract: src.Abstract(),
				Status: src.Status.ToUint8(),
				CreateTime: src.CreateTime.Format(time.DateTime),
				UpdateTime: src.UpdateTime.Format(time.DateTime),
			}
		})}
	ReturnSuccess(ctx, 200, "查询成功", result, 1)
}

func (h *ArticleHandler) Detail(ctx *gin.Context) {
	// 创作者查看自己的文章列表
	idstr:=ctx.Param("id")
	id, err:=strconv.ParseInt(idstr, 10, 64)
	if err!=nil{
		ReturnError(ctx, 4, "参数错误")
		h.l.Error("前端输入的ID不对", logger.Error(err))
		return
	}
	user,ok :=ctx.MustGet("user").(ijwt.UserClaims)
	if !ok {
		ReturnError(ctx, 5, "系统异常")
		h.l.Error("获取用户会话信息失败")
		return
	}
	art, err:= h.svc.GetById(ctx, id)
	if err!=nil{
		ReturnError(ctx, 5, "系统错误")
		h.l.Error("获取文章失败", logger.Error(err))
		return
	}
	// 增加阅读计数
	go func() {
		err1:=h.interSvc.IncrReadCnt(ctx, h.biz, art.Id)
		if err1!=nil{
			h.l.Error("增加阅读计数失败", logger.String("aid:", string(art.Id)), logger.Error(err))
		}
	}()
	// 防止有人窃取数据
	if art.Author.Id!=user.Uid {
		ReturnError(ctx, 4, "非法访问")
		h.l.Warn("非法访问", logger.Field{"uid", user.Uid})
	}
	res:=Result{Data: ArticleVo{
		Id: art.Id,
		Title: art.Title,
		Status: art.Status.ToUint8(),
		Content: art.Content,
		CreateTime: art.CreateTime.Format(time.DateTime),
		UpdateTime: art.UpdateTime.Format(time.DateTime),
	}}
	ReturnSuccess(ctx, 200, "查看个人文章列表成功", res, 1)
}

func (h *ArticleHandler) PubDetail(ctx *gin.Context) {
	idstr:=ctx.Param("id")
	id, err:=strconv.ParseInt(idstr, 10, 64)
	if err!=nil{
		ReturnError(ctx, 4, "参数错误")
		h.l.Error("前端输入的ID不对", logger.Error(err))
		return
	}
	uc:=ctx.MustGet("users").(ijwt.UserClaims)
	var eg errgroup.Group
	var art domain.Article
	eg.Go(func() error {
		art, err=h.svc.GetPublishedById(ctx, id, uc.Uid)
		if err!=nil {
			ReturnError(ctx, 5, "系统异常")
			h.l.Error("获取文章信息失败", logger.Error(err))
			return err
		}
		return nil
	})

	var intr domain.Interactive
	eg.Go(func() error {
		// 要在这里获得这篇文章的详细阅读、点赞、收藏
		c:=ctx.MustGet("claims")
		claims, ok:=c.(*ijwt.UserClaims)
		if !ok {
			ReturnError(ctx, 5, "系统错误")
			h.l.Error("未获取到用户的session信息")
			return errors.New("未获取到用户的session信息")
		}
		intr, err = h.interSvc.GetCnt(ctx, h.biz, id, claims.Uid)
		if err!=nil{
			h.l.Error("获取阅读、点赞、收藏信息失败", logger.Error(err))
		}
		return err
	})
	// 在这里等待前两个协程执行完毕
	err = eg.Wait()
	if err!=nil{
		// 前面查询出错
		ReturnError(ctx, 5, "系统错误")
		return
	}

	// 增加阅读计数
	go func() {
		er:=h.interSvc.IncrReadCnt(ctx, h.biz, art.Id)
		if er!=nil{
			h.l.Error("增加阅读计数失败", logger.Error(er))
		}
	}()
	ctx.JSON(http.StatusOK, Result{
		Data: ArticleVo{
			Id: art.Id,
			Title: art.Title,
			Status: art.Status.ToUint8(),
			Content: art.Content,
			// 作者信息
			AuthorName: art.Author.Name,
			CreateTime: art.CreateTime.Format(time.DateTime),
			UpdateTime: art.UpdateTime.Format(time.DateTime),
			ReadCnt: intr.ReadCnt,
			LikeCnt: intr.LikeCnt,
			Liked: intr.Liked,
			CollectCnt: intr.CollectCnt,
			Collected: intr.Collectd,
		},
	})

}

func (h *ArticleHandler) Like(ctx *gin.Context) {
	var (
		req LikeReq
		err error
	)
	if err=ctx.Bind(&req); err!=nil{
		return
	}
	c:=ctx.MustGet("claims")
	claims, ok:=c.(*ijwt.UserClaims)
	if !ok {
		ReturnError(ctx, 5, "系统错误")
		h.l.Error("未获取到用户的session信息")
		return
	}
	if req.Like {
		err = h.interSvc.Like(ctx, h.biz, req.Id, claims.Uid)
	} else {
		err = h.interSvc.CancelLike(ctx, h.biz, req.Id, claims.Uid)
	}
	if err!=nil {
		ReturnError(ctx, 5, "系统错误")
		return
	}
}